﻿#include "cyclone_parser_base.h"
#include "cpp-gen/FormalParser.h"
#include "estree.hpp"
#include "dummy_node.h"

#include <optional>

#define DO_NOT_LOOK_AHEAD_EOS 1

namespace cyclone::parser
{
    using antlr4::Lexer;
    struct CycloneParserBase::CycloneParserBaseImpl
    {
        bool isInFunction_ = false;
        bool isInClassDeclaration_ = false;
        std::optional<VariableDeclarationModifierKind> variableDeclarionModifierKind_;
    };

    CycloneParserBase::CycloneParserBase(TokenStream *input) : Parser(input)
    {
        impl_.reset(new CycloneParserBaseImpl);
    }
    CycloneParserBase::~CycloneParserBase() {}

    bool CycloneParserBase::p(std::string str) { return prev(str); }

    bool CycloneParserBase::prev(std::string str)
    {
        return _input->LT(-1) ? _input->LT(-1)->getText() == str : false;
    }

    bool CycloneParserBase::n(std::string str)
    {
        return next(str);
    }

    bool CycloneParserBase::next(std::string str)
    {
        return _input->LT(1) ? _input->LT(1)->getText() == str : false;
    }

    bool CycloneParserBase::notLineTerminator()
    {
        return !here(FormalParser::LineTerminator);
    }

    bool CycloneParserBase::notOpenBraceAndNotFunction()
    {
        int nextTokenType = _input->LT(1) ? _input->LT(1)->getType() : -1;
        return nextTokenType != FormalParser::OpenBrace && nextTokenType != FormalParser::Function_;
    }

    bool CycloneParserBase::closeBrace()
    {
        return _input->LT(1) ? _input->LT(1)->getType() == FormalParser::CloseBrace : false;
    }

    bool CycloneParserBase::here(int type)
    {
        // Get the token ahead of the current index.
        if (!getCurrentToken())
            return false;
        auto possibleIndexEosToken = this->getCurrentToken()->getTokenIndex() - 1;
        if (possibleIndexEosToken >= _input->size())
            return false;
        auto ahead = _input->get(possibleIndexEosToken);
        // Check if the token resides on the HIDDEN channel and if it's of the
        // provided type.
        return (ahead->getChannel() == Lexer::HIDDEN) && (ahead->getType() == type);
    }

    bool CycloneParserBase::lineTerminatorAhead()
    {
        // Get the token ahead of the current index.
        if (!getCurrentToken())
            return false;
        int possibleIndexEosToken = this->getCurrentToken()->getTokenIndex() - 1;
        if (possibleIndexEosToken >= _input->size())
            return false;
        auto ahead = _input->get(possibleIndexEosToken);

        if (ahead->getChannel() != Lexer::HIDDEN)
        {
            // We're only interested in tokens on the HIDDEN channel.
            return false;
        }

        if (ahead->getType() == FormalParser::LineTerminator)
        {
            // There is definitely a line terminator ahead.
            return true;
        }

#if DO_NOT_LOOK_AHEAD_EOS
        return false;
#else
        if (ahead->getType() == FormalParser::WhiteSpaces)
        {
            // Get the token ahead of the current whitespaces.
            possibleIndexEosToken = this->getCurrentToken()->getTokenIndex() - 2;
            ahead = _input->get(possibleIndexEosToken);
        }

        // Get the token's text and type.
        std::string text = ahead->getText();
        int type = ahead->getType();
        // Check if the token is, or contains a line terminator.
        return (type == FormalParser::MultiLineComment &&
                (text.find("\r") != std::string::npos || text.find("\n") != std::string::npos)) ||
               (type == FormalParser::LineTerminator);
#endif
    }

    void CycloneParserBase::enterClassDeclaration()
    {
        impl_->isInClassDeclaration_ = true;
    }

    void CycloneParserBase::leaveClassDeclaration()
    {
        impl_->isInClassDeclaration_ = false;
    }

    bool CycloneParserBase::isInClassDeclaration() const
    {
        return impl_->isInClassDeclaration_;
    }

    void CycloneParserBase::enterFunction()
    {
        impl_->isInFunction_ = true;
    }
    void CycloneParserBase::leaveFunction()
    {
        impl_->isInFunction_ = false;
    }
    bool CycloneParserBase::isInFunction() const
    {
        return impl_->isInFunction_;
    }

    void CycloneParserBase::enterVariableDeclaration()
    {
        if (const auto currentToken = getCurrentToken())
        {
            const auto tokenType = currentToken->getType();
            switch (tokenType)
            {
            case FormalParser::Const:
                impl_->variableDeclarionModifierKind_ = VariableDeclarationModifierKind::Const;
                break;
            case FormalParser::Let:
                impl_->variableDeclarionModifierKind_ = VariableDeclarationModifierKind::Let;
                break;
            case FormalParser::Var:
                impl_->variableDeclarionModifierKind_ = VariableDeclarationModifierKind::Var;
                break;
            default:
                break;
            }
        }
    }
    void CycloneParserBase::leaveVariableDeclaration()
    {
        impl_->variableDeclarionModifierKind_.emplace();
    }

    bool CycloneParserBase::variableDeclarationModifierIsConst() const
    {
        return impl_->variableDeclarionModifierKind_.has_value() && VariableDeclarationModifierKind::Const == impl_->variableDeclarionModifierKind_.value();
    }

    bool CycloneParserBase::nodeIsInOptionalChain(antlr4::ParserRuleContext *tree)
    {
        auto verifyCallChain = [](antlr4::ParserRuleContext *node)
        {
            if (nodeIsOptionalChainElement(node))
            {
                if (auto memberExpr = dynamic_cast<FormalParser::MemberExpressionContext *>(node))
                    if (memberExpr->property_ && memberExpr->property_->optional)
                        return true;
                if (auto callExpr = dynamic_cast<FormalParser::CallExpressionContext *>(node))
                    if (callExpr->OptionalChainingOperator())
                        return true;
            }
            return false;
        };

        while (tree)
        {
            if (verifyCallChain(tree))
                return true;
            tree = static_cast<antlr4::ParserRuleContext *>(tree->parent);
        }
        return false;
    }

    bool CycloneParserBase::nodeIsOptionalChainElement(antlr4::ParserRuleContext *node)
    {
        return FormalParser::ContextIdMemberExpression == node->getContextId() || FormalParser::ContextIdCallExpression == node->getContextId();
    }

    bool CycloneParserBase::expectedAssignmentParent(antlr4::ParserRuleContext *node) const
    {
        const auto parentId = node->getContextId();
        return FormalParser::ContextIdPatternInvolvedAssignmentExpression == parentId || FormalParser::ContextIdAssignmentExpression == parentId || FormalParser::ContextIdExpressionStatement == parentId || FormalParser::ContextIdExpressionSequence == parentId;
    }

    bool CycloneParserBase::isFollowedByAssignmentOperator() const
    {
        if (!_input->LT(1))
            return false;
        const auto nextTokenType = _input->LT(1)->getType();
        switch (nextTokenType)
        {
        case FormalParser::Assign:
        case FormalParser::MultiplyAssign:
        case FormalParser::DivideAssign:
        case FormalParser::ModulusAssign:
        case FormalParser::ExponentiationAssign:
        case FormalParser::PlusAssign:
        case FormalParser::MinusAssign:
        case FormalParser::LeftShiftArithmeticAssign:
        case FormalParser::RightShiftArithmeticAssign:
        case FormalParser::RightShiftLogicalAssign:
        case FormalParser::BitAndAssign:
        case FormalParser::BitXorAssign:
        case FormalParser::BitOrAssign:
            return true;
        }
        return false;
    }

    bool CycloneParserBase::isAssignable(antlr4::ParserRuleContext *ctx) const
    {
        const auto contextId = ctx->getContextId();
        switch (contextId)
        {
        case FormalParser::ContextIdPattern:
        case FormalParser::ContextIdIdentifier:
            return true;
        }
#if 0
    if( auto expression = dynamic_cast<FormalParser::ExpressionContext*>(ctx))
        if( "MemberExpression" == expression->type)
            return true;
    return false;
#else
        return reinterpret_cast<FormalParser::ExpressionContext *>(ctx)->type == "MemberExpression";
#endif
    }

    DummyNode *CycloneParserBase::insertDummyNode()
    {
        return _tracker.createInstance<DummyNode>(_ctx);
    }

    void CycloneParserBase::enterPropertyKey()
    {
        this->isParsingProperties_ = true;
    }

    void CycloneParserBase::leavePropertyKey()
    {
        this->isParsingProperties_ = false;
    }

    bool CycloneParserBase::isParsingProperties()
    {
        return this->isParsingProperties_;
    }

    void CycloneParserBase::enterParameter()
    {
        this->isInParameter_ = true;
    }

    void CycloneParserBase::leaveParameter()
    {
        this->isInParameter_ = false;
    }

    bool CycloneParserBase::canBeFollowedByTypeAnnotation()
    {
        return this->impl_->variableDeclarionModifierKind_.has_value() || this->isInParameter_;
    }

    bool CycloneParserBase::checkIsValidRegexFlag(const std::string &string)
    {
        // TODO
        return true;
    }

    bool CycloneParserBase::validateNullishOperatorMixedWithOtherLogicalOp(antlr4::ParserRuleContext *ctx)
    {
        // TODO
        return true;
    }

    void CycloneParserBase::correctNewExpression(antlr4::ParserRuleContext *ctx)
    {
        if (auto newExpr = dynamic_cast<FormalParser::NewExpressionContext *>(ctx))
        {
            auto callee = newExpr->callee;
            if (dynamic_cast<FormalParser::MemberExpressionContext *>(callee) && dynamic_cast<FormalParser::CallExpressionContext *>(static_cast<FormalParser::MemberExpressionContext *>(callee)->object_))
            {
                newExpr->type = "CallExpression";
                static_cast<FormalParser::CallExpressionContext *>(static_cast<FormalParser::MemberExpressionContext *>(callee)->object_)->type = "NewExpression";
            }
        }
    }

}
